<template>
  <div class="pubish-container">
    <el-card>
      <!-- 头部 -->
      <template #header>
        <el-breadcrumb separator-class="el-icon-arrow-right">
          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item>发布文章</el-breadcrumb-item>
        </el-breadcrumb>
      </template>
      <!-- 表单 -->
      <el-form
        ref="publishFormRef"
        :model="publishForm"
        :rules="rules"
        label-width="70px"
      >
        <el-form-item label="标题" prop="title">
          <el-input
            v-model="publishForm.title"
            placeholder="请输入标题"
          ></el-input>
        </el-form-item>
        <el-form-item label="频道" prop="channel_id">
          <el-select
            clearable
            placeholder="请选择频道"
            v-model="publishForm.channel_id"
          >
            <el-option
              v-for="obj in allChannelList"
              :key="obj.id"
              :label="obj.name"
              :value="obj.id"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="封面" prop="cover.type">
          <el-radio-group
            v-model="publishForm.cover.type"
            @change="onCoverTypeChangeFn"
          >
            <el-radio v-for="obj in coverList" :key="obj.id" :label="obj.id">
              {{ obj.name }}
            </el-radio>
          </el-radio-group>
          <!-- 上传图片的结构 -->
          <div class="upload-container" v-if="publishForm.cover.type >= 1">
            <!--
              认识上传组件的属性和事件：
                1. action：必选参数，上传的地址
                2. list-type: 文件列表的类型
                3. headers： 设置上传的请求头部
                4. name:  上传的文件字段名
                5. limit: 最大允许上传个数
                6. disabled 是否禁用
                7. file-list 上传的文件列表
           -->
            <el-upload
              :action="VUE_APP_UPLOAD_URL"
              list-type="picture-card"
              name="image"
              :file-list="showFileList"
              :on-remove="onUploadRemoveFn"
              :on-success="onUploadSuccessFn"
              :limit="publishForm.cover.type"
              :on-exceed="onUploadExceedFn"
              :before-upload="onBeforeUploadFn"
            >
              <i class="el-icon-plus"></i>
            </el-upload>
          </div>
        </el-form-item>
        <el-form-item label="内容" prop="content">
          <!-- 富文本编辑器 -->
          <quill-editor
          :value='publishForm.content'
          @change="onEditorChangeFn($event)" />
        </el-form-item>
        <el-form-item>
          <!-- 发布按钮 -->
          <div class="publish-btn" v-if="!$route.query.targetId">
            <el-button type="primary" @click="publishArticleFn(false)">
              发布文章</el-button
            >
            <el-button @click="publishArticleFn(true)">存入草稿</el-button>
          </div>
          <!-- 修改按钮 -->
          <div class="update-btn" v-else>
            <el-button type="primary" @click="updateArticleFn">
              修改文章</el-button
            >
            <el-button>重置</el-button>
          </div>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>

<script>
// 目标：富文本编辑器结构
// 1. 找到相关的第三方组件，可以去找里找，https://github.com/vuejs/awesome-vue
//    收集了很多vue的第三方模块包
// 2.找到这个模块包的文档，根据文档指示，进行

// 目标：收集表单数据
// 1. 准备表单对象，对象的属性与后台接口字段保持一致
// 2. 通过v-model绑定表单对象的指定属性
// 3. 富文本编辑器，问题，默认是会包含标签，通过查文档，
// 不写v-model，而是通过监听change事件，拿到不包含标签的内容把
// \n替换掉，再赋值给表单对象的content属性

// 目标：实现图片上传
// 1. el-upload组件铺出来了，认识了属性
// 2. 对接后台接口
// 3. 图片上传成功了，需要把后台返回的图片地址存储到cover.images数组中，
//  今后提交给后台

// 目标：控制上传的图片张数 和 删除图片，需要让cover.images数组保持同步
// 1. 控制上传的图片张数：type 的值 和 images一致即可，limit属性做一个限制，
// 并且可以通过:on-exceed属性监听张数超出，给出提示

// 2.删除以上传的图片，需要把它对应的url地址从cover.images数组中删掉

// 目标：实现单图和三图切换的images数组保持同步
// 1. 监听单选框组的change事件
// 2. 判断images数组有值，则取出第一个在赋予回去

// 目标：三图切换到单图，数据变了，应该也要让视图保持同步
// 1. 通过查文档，视图展示的文件列表是通过file-list属性控制的
// 需要通过cover.images数组得到一个计算属性，绑定在file-list属性上,
// file-list的数据格式是数组包对象，对象至少包含一个name和url属性
// 2.需要让cover.images数组的 变化驱动视图更新

// 目标：封面上传之前，做个限制，判断
// 1. 文件类型，必须是图片，png、jpg、jgep
// 2. 文档大小，不能超过1M

// 目标：发布文章 -> 成功了，跳转到内容管理页
// 1. 给按钮绑定点击事件
// 2.定义接口 -> 导接口 -> 调用接口
// 3.成功了，跳转到内容管理页

// 目标：发布文章之前，做校验，通过了，在发给后台
// 1. 准备必要的数据 -> model，rules，prop，v-model
// 2. 整体（兜底）校验  -> el-form -> ref属性 -> validate(valid => {})
// 3. 遇到了问题： quill-editor不属于element-ui体系的，默认的表单校验不触发，
// 解决方式：获取el-form表单组件对象，在quill-editor组件change事件中，手动调用
// el-form的validateField方法，主动触发校验

// 目标：封面的校验
// 1. 自定义校验规则 { validator: (rule, value, callback) => {
// if(通过了){ callback()}
// else { callback(new Error('错误信息'))}
// }}
// 2.封面的检验需要手动关闭校验几个场景：
// 1. 类型切换的时候
// 2.图片上传成功之后
// 3.删除图片之后
// 3. 关闭校验的条件 =>  cover.type === cover.images.length

// 目标：整体/兜底校验
// 1. 给el-form添加ref属性并取个名字
// 2.在发送请求之前，做个整体校验，
// this.$refs.ref值.validate(valid => {
//   if(valid) {
// 通过
//   }else {
// 不通过
//   }
// })

// 目标：完成文章修改操作
// 1. 给按钮绑定点击事件
// 2.定义接口 -> 调接口 -> 传参数
// 3.跳转到内容管理页面
// 4. 做了些判断
// 1. targetId有值，才需要获取文章详情
// 2.准备了两套按钮，一套是发布的，一套是修改的，
//   二者需要互斥显示，v-if&v-else配合targetId有没有值即可搞定

import { ARTICLE_COVER_LIST } from '@/constant'
import { getAllChannelListAPI } from '@/api/Article'
import { VUE_APP_UPLOAD_URL } from '@/app.config'

import {
  publishArticleAPI,
  getArticleDetailAPI,
  updateArticleAPI
} from '@/api/Publish'

export default {
  name: 'Publish',
  data () {
    return {
      VUE_APP_UPLOAD_URL,
      coverList: ARTICLE_COVER_LIST, // 文章封面列表
      allChannelList: [], // 所有的频道列表
      // 发布文章的表单对象
      publishForm: {
        title: '',
        content: '',
        cover: {
          // 封面
          type: 1, // 类型
          images: [] // 图片地址
        },
        channel_id: 0
      },
      // 发布文章的表单对象校验规则
      rules: {
        title: [
          {
            required: true,
            message: '请输入标题',
            trigger: ['blur', 'change']
          },
          {
            min: 1,
            max: 10,
            message: '标题长度在1 - 10个字符',
            trigger: ['blur', 'change']
          }
        ],
        content: [
          {
            required: true,
            message: '请输入内容',
            trigger: ['blur', 'change']
          },
          {
            min: 10,
            max: 100,
            message: '标题长度在10 - 100个字符',
            trigger: ['blur', 'change']
          }
        ],
        'cover.type': [
          {
            // rule: 检验规则对象
            // value: 当前检验字段的值
            // callback: 回调函数
            // 如果校验出错，callback(new Error(错误原因))
            // 如果校验通过，callback()
            // 自定义检验规则
            validator: (rule, value, callback) => {
              if (
                value >= 1 &&
                value !== this.publishForm.cover.images.length
              ) {
                callback(new Error('请选择封面'))
              } else {
                callback()
              }
            },
            trigger: 'change'
          }
        ],
        channel_id: [
          {
            required: true,
            message: '请选择频道',
            trigger: ['blur', 'change']
          }
        ]
      }
    }
  },
  async created () {
    // 获取所有频道列表
    const resp = await getAllChannelListAPI()
    this.allChannelList = resp.data.data.channels
    // 获取文章详情
    // targetId有值，说明是从编辑页过来的，再去获取文章详情
    const { targetId } = this.$route.query
    if (!targetId) return
    const resp2 = await getArticleDetailAPI({
      artId: targetId
    })
    this.publishForm = {
      ...this.publishForm,
      ...resp2.data.data
    }
  },
  computed: {
    // 计算得到的展示的文件列表
    showFileList () {
      return this.publishForm.cover.images.map((url, index) => {
        return {
          name: `item${index + 1}.jpg`,
          url
        }
      })
    }
  },
  methods: {
    // 修改文章
    updateArticleFn () {
      this.$refs.publishFormRef.validate(async (valid) => {
        if (!valid) return
        // 调用修改文章接口
        await updateArticleAPI({
          artId: this.$route.query.targetId,
          ...this.publishForm
        })
        this.$router.push('/layout/article')
      })
    },
    // 封面上传前的钩子，返回false则停止上传，返回true则继续
    onBeforeUploadFn (file) {
      const { name, size } = file
      // 1. 判断类型
      if (!/\.(png|jpe?g)$/i.test(name)) {
        this.$message({
          type: 'warning',
          message: '仅支持png、jpg、jpeg图片格式'
        })
        return false
      }
      // 2.判断大小
      // 1KB = 1024B
      // 1M = 1024KB
      // 1G = 1024M
      if (size / 1024 / 1024 > 1) {
        this.$message({
          type: 'warning',
          message: '图片太大、不能超过1M'
        })
        return false
      }
      return true
    },
    // 封面类型切换事件
    onCoverTypeChangeFn () {
      if (this.publishForm.cover.images.length) {
        this.publishForm.cover.images = [this.publishForm.cover.images[0]]
      }
      if (
        this.publishForm.cover.type === this.publishForm.cover.images.length
      ) {
        this.$refs.publishFormRef.clearValidate('cover.type')
      }
    },
    // 封面上传成功的回调
    onUploadSuccessFn (resp) {
      this.publishForm.cover.images.push(resp.data.url)
      // 上传成功之后，清除校验
      if (
        this.publishForm.cover.type === this.publishForm.cover.images.length
      ) {
        this.$refs.publishFormRef.clearValidate('cover.type')
      }
    },
    // 富文本编辑器改变事件
    onEditorChangeFn ({ text }) {
      // replaceAll替换所有,replace只替换第一个匹配的
      this.publishForm.content = text.replaceAll('\n', '')
      this.$refs.publishFormRef.validateField('content')
    },
    // 发布文章
    async publishArticleFn (draft) {
      // 整体校验，都通过了，再发请求
      this.$refs.publishFormRef.validate(async (valid) => {
        if (!valid) return
        await publishArticleAPI({ ...this.publishForm, draft })
        this.$message({ type: 'success', message: '发布成功' })
        this.$router.push('/layout/article')
      })
    },
    // 超出张数限制触发的钩子
    onUploadExceedFn () {
      this.$message({
        type: 'warning',
        message: `当前最多只能上传 ${this.publishForm.cover.type} 张`
      })
    },
    // 封面删除的回调
    onUploadRemoveFn (file) {
      // filter 或者 indexOf + splice
      this.publishForm.cover.images = this.publishForm.cover.images.filter(
        (url) => url !== file.url
      )
      if (
        this.publishForm.cover.type === this.publishForm.cover.images.length
      ) {
        // 关闭校验
        this.$refs.publishFormRef.clearValidate('cover.type')
      } else {
        // 开启校验
        this.$refs.publishFormRef.validateField('cover.type')
      }
    }
  }
}
</script>

<style>
.ql-container {
  height: 300px !important;
}
</style>
